package main

import (
	"mmio"

	"stm32/hal/raw/tim"
)

// setupPulsePWM setups the timer t to perform pulse PWM on all 4 channels.
// Pulse PWM uses timer in one pulse mode to generate one period of PWM.
// Software is responsible to set desired duty cycle and start pulse. Then
// timer is responsible to generate one period of PWM. Pulse PWM is intended for
// applications were there is important to leave PWM in known (disabled) state
// in case of software crasch or lookup (eg. water heating).
//
// pclk is peripheral clock (use system.APBx.Clock() to obtain correct value,
// check out doc to find out to which bus the timer t is connected to), periodms
// is PWM period in millisecond, maxCCR is CCR value that should correspond to
// 100% duty cycle (PWM full on). The PWM resolution is equal to maxCCR+1.
// Because pclk is usually multiple of 10^6 Hz and it is multipled by period and
// divided by resolution to calculate the prescaler value, it is good to use
// 10^n * periodms/m - 1 as maxCCR to avoid rounding errors.
//
// The timer is configured in center-aligned PWM, one-pulse mode. Pulses should
// be generated by software using [store CCRx, set UG, set CEN] sequence. This
// allows the timer to only counting up and stops count at ARR value without
// reseting CNT (in edge-aligned mode CNT is reset to 0 after overflow which
// means that for CCRx > 0 channel x remains in active state after pulse
// generation). Ensure CCRx value is always less than maxCCR then the x channel
// will be always set inactive at the end of the pulse and stays inactive until
// the next pulse will be generated. This ensures automatic disable of
// controlled device (eg: heater) after most software crashes.
func setupPulsePWM(t *tim.TIM_Periph, pclk uint, periodms, maxCCR int) {
	t.PSC.U16.Store(uint16(int(pclk/1000)*periodms/(maxCCR+1) - 1))
	t.ARR.U32.Store(uint32(maxCCR))
	t.CCMR1.Store(6<<tim.OC1Mn | 6<<tim.OC2Mn | tim.OC1PE | tim.OC2PE)
	t.CCMR2.Store(6<<tim.OC3Mn | 6<<tim.OC4Mn | tim.OC3PE | tim.OC4PE)
	t.CCR1.Store(0)
	t.CCR2.Store(0)
	t.CCR3.Store(0)
	t.CCER.SetBits(tim.CC1E | tim.CC2E | tim.CC3E)
	t.CR1.Store(tim.OPM | tim.CMS | tim.URS)
	t.DIER.Store(tim.UIE)
}

type PulsePWM3 struct {
	t        *tim.TIM_Periph
	ccr      [3]*mmio.U32
	lastSwap int
	chmax    uint32
}

// Init initialises p. t must be initialiset using setupPulsePWM before pass it
// to Init.
func (p *PulsePWM3) Init(t *tim.TIM_Periph) {
	p.t = t
	p.ccr[0] = &t.CCR1.U32
	p.ccr[1] = &t.CCR2.U32
	p.ccr[2] = &t.CCR3.U32
	p.chmax = t.ARR.U32.Load() - 1 // Always disable after pulse generation.
}

func (p *PulsePWM3) Timer() *tim.TIM_Periph {
	return p.t
}

// Max returns maximum value that passed to Set enables 100% all 3 channels.
func (p *PulsePWM3) Max() int {
	return int(3 * p.chmax)
}

func (p *PulsePWM3) Set(v int) {
	v32 := uint32(v)
	max := p.chmax
	switch {
	case v32 <= max:
		p.ccr[0].Store(v32)
		p.ccr[1].Store(0)
		p.ccr[2].Store(0)
		if p.lastSwap != 0 {
			p.ccr[1], p.ccr[2] = p.ccr[2], p.ccr[1]
			p.lastSwap = 0
		}
	case v32 <= 2*max:
		p.ccr[0].Store(max)
		p.ccr[1].Store(v32 - max)
		p.ccr[2].Store(0)
	case v32 <= 3*max:
		p.ccr[0].Store(max)
		p.ccr[1].Store(max)
		p.ccr[2].Store(v32 - 2*max)
		if p.lastSwap != 1 {
			p.ccr[0], p.ccr[1] = p.ccr[1], p.ccr[0]
			p.lastSwap = 1
		}
	default:
		p.ccr[0].Store(max)
		p.ccr[1].Store(max)
		p.ccr[2].Store(max)
		if p.lastSwap != 2 {
			p.ccr[0], p.ccr[1], p.ccr[2] = p.ccr[1], p.ccr[2], p.ccr[0]
			p.lastSwap = 2
		}
	}
}

func (p *PulsePWM3) Pulse() {
	p.t.EGR.Store(tim.UG)
	p.t.CEN().Set()
}

func (p *PulsePWM3) ClearIF() {
	p.t.SR.Store(0)
}
